;(function(factory) {
    window.Driver = factory();
})(function () {
    "use strict";

    function _typeof(obj) { "@babel/helpers - typeof"; if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); }

    function _possibleConstructorReturn(self, call) { if (call && (_typeof(call) === "object" || typeof call === "function")) { return call; } return _assertThisInitialized(self); }

    function _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return self; }

    function _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); }

    function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function"); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } }); if (superClass) _setPrototypeOf(subClass, superClass); }

    function _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); }

    function _instanceof(left, right) { if (right != null && typeof Symbol !== "undefined" && right[Symbol.hasInstance]) { return !!right[Symbol.hasInstance](left); } else { return left instanceof right; } }

    function _classCallCheck(instance, Constructor) { if (!_instanceof(instance, Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

    function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }

    function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; }

    /**
     * 事务的模式
     * READ：读
     * READ_WRITE：读写
     * VERION_CHANGE: 版本变更
     * @type {{READ: string, READ_WRITE: string, VERION_CHANGE: string}}
     */
    var TRANSACTION_MODE = {
        READ_ONLY: 'readonly',
        READ_WRITE: 'readwrite',
        READ_WRITE_FLUSH: 'readwriteflush ',
        VERION_CHANGE: 'verionchange'
    };
    /*
     * 游标循环的方向
     * CursorDirection.NEXT                     顺序循环
     * CursorDirection.NEXT_NO_DUPLICATE        顺序循环不重复.当取值有重复时，只获取一次。
     * CursorDirection.PREV                     倒序循环
     * CursorDirection.PREV_NO_DUPLICATE        倒序循环不重复.当取值有重复时，只获取一次。
     * @type {{NEXT: *, NEXT_NO_DUPLICATE: *, PREV: *, PREV_NO_DUPLICATE: *}}
     */

    window.CURSOR_DIRECTION = {
        NEXT: IDBCursor.NEXT,
        NEXT_NO_DUPLICATE: IDBCursor.NEXT_NO_DUPLICATE,
        PREV: IDBCursor.PREV,
        PREV_NO_DUPLICATE: IDBCursor.PREV_NO_DUPLICATE
    };
    window.KeyRange = {
        leq: function leq(x) {
            return DBKeyRange.upperBound(x);
        },
        //All keys ≤ x
        lt: function lt(x) {
            return DBKeyRange.upperBound(x, true);
        },
        //keys < x
        geq: function geq(x) {
            return DBKeyRange.lowerBound(x);
        },
        //keys ≥ x
        gt: function gt(x) {
            return DBKeyRange.lowerBound(x, true);
        },
        //keys > x
        between: function between(x, y) {
            return DBKeyRange.bound(x, y);
        },
        //keys ≥ x && ≤ y
        gt_lt: function gt_lt(x, y) {
            return DBKeyRange.bound(x, y, true, true);
        },
        //keys > x &&< y
        gt_leq: function gt_leq(x, y) {
            return DBKeyRange.bound(x, y, true, false);
        },
        //keys > x && ≤ y
        geq_lt: function geq_lt(x, y) {
            return DBKeyRange.bound(x, y, false, true);
        },
        //keys ≥ x &&< y
        eq: function eq(x) {
            return DBKeyRange.only(x);
        } //key = x

    };
    var innerDB = window.indexedDB || window.mozIndexedDB || window.webkitIndexedDB || window.msIndexedDB;
    var DBTransaction = window.IDBTransaction || window.webkitIDBTransaction || window.msIDBTransaction;
    var DBKeyRange = window.IDBKeyRange || window.webkitIDBKeyRange || window.msIDBKeyRange;
    window.INDEXDB_STATUES = {
        CONNECT: {
            ING: 0,
            SUCCESS: 1,
            ERROR: 2,
            CHANGE: 3,
            CLOSE: 4
        },
        DATABASE: {
            ERROR: 0
        }
    };

    var _instance = function _instance(example, type) {
        var pro = type.prototype;
        example = example.__proto__;

        while (true) {
            if (example === null) return false;
            if (pro === example) return true;
            example = example.__proto__;
        }
    };

    var _copy = function _copy(src, target) {
        if (!target) return src;

        for (var prop in src) {
            target[prop] = src[prop];
        }

        return target;
    };

    var Assist =
        /*#__PURE__*/
        function () {
            function Assist() {
                _classCallCheck(this, Assist);
            }

            _createClass(Assist, null, [{
                key: "isTrue",
                value: function isTrue(assist, error) {
                    if (!assist) {
                        throw new Error(error);
                    }
                }
            }]);

            return Assist;
        }();

    var Database =
        /*#__PURE__*/
        function () {
            function Database(name) {
                var _arguments = arguments;
                var version = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 1;

                _classCallCheck(this, Database);

                Assist.isTrue(name, 'Database name lost');
                this.name = name;
                this.version = version;
                this.connect = null; //数据库连接

                this.events = {
                    process: function process(status) {},
                    //数据库过程事件
                    success: function success() {
                        console.debug("success");
                    },
                    //数据库打开事件
                    close: function close() {
                        console.debug("close");
                    },
                    //数据库关闭
                    change: function change() {
                        console.debug("change");
                    },
                    //版本改变
                    error: function error() {
                        console.debug(_arguments);
                    } //数据库打开链接错误或者数据库错误

                };
            }

            _createClass(Database, [{
                key: "drop",
                value: function drop() {
                    this.close();
                    var request = innerDB.deleteDatabase(this.name);
                    return new Promise(function (resolve, reject) {
                        request.onerror = function (event) {
                            reject(event);
                        };

                        request.onsuccess = function (event) {
                            resolve(event);
                        };
                    });
                }
            }, {
                key: "on",
                value: function on(event, fn) {
                    var _this = this;

                    Assist.isTrue(event && fn, 'Event type and callback lost');

                    if (_instance(event, String)) {
                        if (event in this.events) {
                            this.events[event] = fn;
                        } else {
                            throw new Error("Unsupport event type ".concat(event, "!"));
                        }
                    } else {
                        Object.keys(event).forEach(function (fn, e) {
                            return _this.on(e, fn);
                        });
                    }

                    return this;
                }
            }, {
                key: "isOpen",
                value: function isOpen() {
                    return this.connect != null;
                }
            }, {
                key: "open",
                value: function open(name, version) {
                    var _this2 = this;

                    this.name = name = name || this.name;
                    this.version = version = version || this.version;
                    var request = innerDB.open(name, version);
                    this.events.process(INDEXDB_STATUES.CONNECT.ING);

                    request.onerror = function (e) {
                        var error = e.target.error;

                        _this2.events.process(INDEXDB_STATUES.CONNECT.ERROR);

                        _this2.events.error.call(_this2, error);
                    };

                    request.onclose = function (e) {
                        this.events.process(INDEXDB_STATUES.CONNECT.CLOSE);
                        this.events.close.call(this, e.target.result);
                    };

                    return new Promise(function (resolve) {
                        request.onsuccess = function (e) {
                            _this2.connect = e.target.result;

                            _this2.events.process(INDEXDB_STATUES.CONNECT.SUCCESS);

                            _this2.connect.onerror = _this2.connect.onabort = function (e) {
                                var error = e.target.error;

                                _this2.events.process(INDEXDB_STATUES.CONNECT.SUCCESS, INDEXDB_STATUES.DATABASE.ERROR);

                                _this2.events.error.call(_this2, error);
                            };

                            _this2.events.success.call(_this2, _this2.connect);

                            resolve(_this2);
                        };

                        request.onupgradeneeded = function (e) {
                            _this2.events.process(INDEXDB_STATUES.CONNECT.SUCCESS);

                            _this2.connect = e.target.result;

                            _this2.events.change.call(_this2, _this2.connect);

                            resolve(_this2);
                        };
                    });
                }
            }, {
                key: "close",
                value: function close() {
                    if (this.isOpen()) {
                        this.events.process(INDEXDB_STATUES.CONNECT.CLOSE);
                        this.connect.close();
                        this.events.close.call(this, this.connect);
                        this.connect = null;
                    }
                }
                /**
                 * 只能在open的时候调用
                 * @param table {
                 *      name:'xx',
                 *      primaryKey: 'id',
                 *      autoIncrement: true|false，
                 *      indexes: [
                 *          {name: 'xxx', column: 'id', unique: true|false, multiEntry: true|false},
                 *      ],
                 *      data: [{}]
                 *      }
                 */

            }, {
                key: "createTable",
                value: function createTable(table) {
                    Assist.isTrue(table !== undefined, 'Table can not specify');

                    if (_instance(table, String)) {
                        table = {
                            name: table
                        };
                    }

                    return new Table(table, this);
                }
                /**
                 * 只能在open的时候调用
                 * @param tableName
                 */

            }, {
                key: "dropTable",
                value: function dropTable(tableName) {
                    return this.connect.deleteObjectStore(tableName);
                }
            }, {
                key: "table",
                value: function table(tableName) {
                    if (!this.exists(tableName)) {
                        throw new Error("table ".concat(tableName, " not exists!"));
                    }

                    return new Table({
                        name: tableName
                    }, this);
                }
            }, {
                key: "exists",
                value: function exists(table) {
                    var tables = this.tables();
                    return tables && tables.contains(table);
                }
            }, {
                key: "tables",
                value: function tables() {
                    return this.connect['objectStoreNames'];
                }
            }]);

            return Database;
        }();

    var Index = function Index(name, column) {
        var unique = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
        var multiEntry = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : false;

        _classCallCheck(this, Index);

        this.name = name;
        this.column = column;
        this.unique = !!unique;
        this.multiEntry = !!multiEntry;
    };

    var _getObjectStore = function _getObjectStore(table) {
        var mode = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : TRANSACTION_MODE.READ_ONLY;
        Assist.isTrue(_instance(table, Table), "Type error");
        var connect = table.database.connect;
        var trans = connect.transaction(table.name, mode);

        trans.oncomplete = function (e) {
            return console.debug(e);
        };

        trans.onerror = function (e) {
            return console.debug(e);
        };

        return trans.objectStore(table.name);
    };

    var _request = function _request(request, success) {
        return new Promise(function (resolve, reject) {
            request.onsuccess = function (e) {
                !success ? resolve(e.target.result) : success(e.target.result, resolve);
            };

            request.onerror = function (e) {
                reject(e.target.error, e.target.result);
            };
        });
    };

    var Table =
        /*#__PURE__*/
        function () {
            /**
             *
             * @param name 表名
             * @param primaryKey 主键字段
             * @param autoIncrement 是否自动增长
             * @param indexes 表索引 @see TableIndex
             * @param data 表初始化数据
             * @param database 数据库对象 @see Database
             */
            function Table(_ref, database) {
                var name = _ref.name,
                    _ref$primaryKey = _ref.primaryKey,
                    primaryKey = _ref$primaryKey === void 0 ? 'id' : _ref$primaryKey,
                    _ref$autoIncrement = _ref.autoIncrement,
                    autoIncrement = _ref$autoIncrement === void 0 ? false : _ref$autoIncrement,
                    _ref$indexes = _ref.indexes,
                    indexes = _ref$indexes === void 0 ? [] : _ref$indexes,
                    _ref$data = _ref.data,
                    data = _ref$data === void 0 ? [] : _ref$data;

                _classCallCheck(this, Table);

                this.name = name;
                this.primaryKey = primaryKey;
                this.autoIncrement = !!autoIncrement;
                Assist.isTrue(_instance(database, Database) && database.isOpen(), "Database type error");
                this.database = database;

                if (!database.exists(name)) {
                    var store = database.connect.createObjectStore(name, {
                        keyPath: this.primaryKey,
                        autoIncrement: autoIncrement
                    });
                    indexes.forEach(function (_ref2) {
                        var name = _ref2.name,
                            column = _ref2.column,
                            unique = _ref2.unique,
                            multiEntry = _ref2.multiEntry;
                        return store.createIndex(name, column, {
                            unique: unique,
                            multiEntry: multiEntry
                        });
                    });
                    data.forEach(function (item) {
                        return store.add(item);
                    });
                }
            }
            /**
             *
             * 插入数据 对象或者数据数组
             * @param data obj or arr
             */


            _createClass(Table, [{
                key: "insert",
                value: function insert(data) {
                    var arr = _instance(data, Array) ? data : [data];

                    var store = _getObjectStore(this, TRANSACTION_MODE.READ_WRITE);

                    return new Promise(function (resolve) {
                        var results = [];
                        arr.forEach(function (item, index) {
                            _request(store.add(item)).then(function (rs) {
                                results.push(rs);
                                if (index + 1 === arr.length) resolve(results);
                            });
                        });
                    });
                }
                /**
                 * 当where未指定 直接更新modify
                 * @param modify 更新内容 {xxx:xx}
                 * @param where 条件 keyRange or key
                 */

            }, {
                key: "update",
                value: function update(modify, where) {
                    var store = _getObjectStore(this, TRANSACTION_MODE.READ_WRITE);

                    if (!where) return _request(store.put(modify));else return new Promise(function (resolve, reject) {
                        _request(store.getAll(where)).then(function (rs) {
                            rs = _instance(rs, Array) ? rs : [rs];
                            var rows = [];
                            rs.forEach(function (item, index) {
                                _request(store.put(_copy(modify, item))).then(function (r) {
                                    rows.push(r);
                                    if (index + 1 === rs.length) resolve(rows);
                                });
                            });
                        }).catch(function (err) {
                            return reject(err);
                        });
                    });
                }
                /**
                 * 根据主键值删除
                 * @param key
                 */

            }, {
                key: "delete",
                value: function _delete(key) {
                    _request(_getObjectStore(this, TRANSACTION_MODE.READ_WRITE).delete(key));
                }
                /**
                 * 清空表
                 */

            }, {
                key: "clear",
                value: function clear() {
                    _request(_getObjectStore(this, TRANSACTION_MODE.READ_WRITE).clear());
                }
                /**
                 * 根据索引名称删除索引
                 * @param name
                 */

            }, {
                key: "dropIndex",
                value: function dropIndex(name) {
                    _getObjectStore(this, TRANSACTION_MODE.READ_WRITE).deleteIndex(name);
                }
                /**
                 * 获取指定范围内的主键列表
                 * @param keyRange @link keyRange
                 * @param limit
                 */

            }, {
                key: "keys",
                value: function keys(keyRange, limit) {
                    Assist.isTrue(_instance(keyRange, DBKeyRange), "");

                    var store = _getObjectStore(this);

                    var request = store.getAllKeys(keyRange, limit);
                    return _request(request);
                }
                /**
                 * 统计数量
                 * @param keyRange @link keyRange
                 */

            }, {
                key: "count",
                value: function count(keyRange) {
                    var request = _getObjectStore(this).count(keyRange);

                    return _request(request);
                }
                /**
                 *
                 * @param keyRange A value that is or resolves to an KeyRange.
                 * @param limit Specifies the number of values to return if more than one is found.
                 * @returns {*}
                 */

            }, {
                key: "query",
                value: function query(keyRange, limit) {
                    var request = _getObjectStore(this).getAll(keyRange, limit);

                    return _request(request);
                }
            }, {
                key: "queryByKey",
                value: function queryByKey(key) {
                    var request = _getObjectStore(this).get(key);

                    return _request(request);
                }
                /**
                 *
                 * @param keyRange @see KEY_RANGE
                 * @param direction @see CURSOR_DIRECTION
                 */

            }, {
                key: "fetch",
                value: function fetch(keyRange, direction) {
                    var store = _getObjectStore(this);

                    var request = store.openCursor(null, direction);
                    return new Promise(function (resolve, reject) {
                        var rows = [];

                        _request(request, function (cursor) {
                            if (!cursor) {
                                resolve(rows);
                                return;
                            }

                            rows.push(cursor.value);
                            cursor.continue();
                        }).catch(function (err) {
                            return reject(err);
                        });
                    });
                }
                /**
                 *
                 * @param name indexName
                 * @param key key or keyRange
                 */

            }, {
                key: "queryByIndex",
                value: function queryByIndex(name, key) {
                    var store = _getObjectStore(this);

                    var index = store.index(name);
                    return _request(index.getAll(key));
                }
                /**
                 * 获取的同时删除 消费掉表内的数据
                 * direction: @see CURSOR_DIRECTION
                 */

            }, {
                key: "deplete",
                value: function deplete() {
                    var direction = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : CURSOR_DIRECTION.NEXT;

                    var store = _getObjectStore(this, TRANSACTION_MODE.READ_WRITE);

                    var request = store.openCursor(null, direction);
                    return new Promise(function (resolve, reject) {
                        var rows = [];

                        _request(request, function (cursor) {
                            if (!cursor) {
                                resolve(rows);
                                return;
                            }

                            rows.push(cursor.value);
                            cursor.continue();
                            store.delete(cursor.key);
                        }).catch(function (err) {
                            return reject(err);
                        });
                    });
                }
                /**
                 * use index and cursor
                 */

            }, {
                key: "multiple",
                value: function multiple(indexName, keyRange) {
                    var cursorDirection = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : CURSOR_DIRECTION.NEXT;

                    var store = _getObjectStore(this);

                    var index = store.index(indexName);
                    var request = index.openCursor(keyRange, cursorDirection);
                    return new Promise(function (resolve, reject) {
                        var array = [];

                        _request(request, function (cursor) {
                            if (!cursor) {
                                resolve(array);
                                return;
                            }

                            array.push(cursor.value);
                            cursor.continue();
                        }).catch(function (err) {
                            return reject(err);
                        });
                    });
                }
            }]);

            return Table;
        }();

    var Driver =
        /*#__PURE__*/
        function (_Database) {
            _inherits(Driver, _Database);

            function Driver() {
                _classCallCheck(this, Driver);

                return _possibleConstructorReturn(this, _getPrototypeOf(Driver).apply(this, arguments));
            }

            _createClass(Driver, [{
                key: "insert",
                value: function insert(table, data) {
                    return this.table(table).insert(data);
                }
            }, {
                key: "select",
                value: function select(table, keyRange, limit) {
                    return this.table(table).query(keyRange, limit);
                }
            }, {
                key: "selectByKey",
                value: function selectByKey(table, key) {
                    return this.table(table).queryByKey(key);
                }
            }, {
                key: "count",
                value: function count(table, keyRange) {
                    return this.table(table).count(keyRange);
                }
            }, {
                key: "update",
                value: function update(table, modify, where) {
                    return this.table(table).update(modify, where);
                }
            }, {
                key: "delete",
                value: function _delete(table, key) {
                    return this.table(table).delete(key);
                }
            }, {
                key: "truncate",
                value: function truncate(table) {
                    return this.table(table).clear();
                }
            }]);

            return Driver;
        }(Database);

    return Driver;
})
